package mdb.com.vn.utils;

import java.text.ChoiceFormat;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * Helper methods to deal with date/time formatting with a specific defined
 * format (<a href="http://www.w3.org/TR/NOTE-datetime">ISO8601</a>) or a
 * plurialization correct elapsed time in minutes and seconds.
 * 
 * 
 * @since Ant 1.5
 * 
 * @version $Revision: 1.11.2.4 $
 */
public final class DateUtils {
	public static final String pattern = "yyyyMMddHHmmss";

	/**
	 * ISO8601-like pattern for date-time. It does not support timezone.
	 * <tt>yyyy-MM-ddTHH:mm:ss</tt>
	 */
	public static final String ISO8601_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss";

	/**
	 * ISO8601-like pattern for date. <tt>yyyy-MM-dd</tt>
	 */
	public static final String ISO8601_DATE_PATTERN = "yyyy-MM-dd";

	/**
	 * ISO8601-like pattern for time. <tt>HH:mm:ss</tt>
	 */
	public static final String ISO8601_TIME_PATTERN = "HH:mm:ss";

	/**
	 * Format used for SMTP (and probably other) Date headers.
	 */
	public static final DateFormat DATE_HEADER_FORMAT = new SimpleDateFormat(
			"EEE, dd MMM yyyy HH:mm:ss ", Locale.US);

	// code from Magesh moved from DefaultLogger and slightly modified
	private static final MessageFormat MINUTE_SECONDS = new MessageFormat(
			"{0}{1}");

	private static final double[] LIMITS = { 0, 1, 2 };

	private static final String[] MINUTES_PART = { "", "1 minute ",
			"{0,number} minutes " };

	private static final String[] SECONDS_PART = { "0 seconds", "1 second",
			"{1,number} seconds" };

	private static final ChoiceFormat MINUTES_FORMAT = new ChoiceFormat(LIMITS,
			MINUTES_PART);

	private static final ChoiceFormat SECONDS_FORMAT = new ChoiceFormat(LIMITS,
			SECONDS_PART);

	static {
		MINUTE_SECONDS.setFormat(0, MINUTES_FORMAT);
		MINUTE_SECONDS.setFormat(1, SECONDS_FORMAT);
	}

	/** private constructor */
	private DateUtils() {
	}

	/**
	 * Format a date/time into a specific pattern.
	 * 
	 * @param date
	 *            the date to format expressed in milliseconds.
	 * @param pattern
	 *            the pattern to use to format the date.
	 * @return the formatted date.
	 */
	public static String format(long date, String pattern) {
		return format(new Date(date), pattern);
	}

	/**
	 * Format a date/time into a specific pattern.
	 * 
	 * @param date
	 *            the date to format expressed in milliseconds.
	 * @param pattern
	 *            the pattern to use to format the date.
	 * @return the formatted date.
	 */
	public static String format(Date date, String pattern) {
		DateFormat df = createDateFormat(pattern);
		return df.format(date);
	}

	/**
	 * Format an elapsed time into a plurialization correct string. It is
	 * limited only to report elapsed time in minutes and seconds and has the
	 * following behavior.
	 * <ul>
	 * <li>minutes are not displayed when 0. (ie: "45 seconds")</li>
	 * <li>seconds are always displayed in plural form (ie "0 seconds" or
	 * "10 seconds") except for 1 (ie "1 second")</li>
	 * </ul>
	 * 
	 * @param millis
	 *            the elapsed time to report in milliseconds.
	 * @return the formatted text in minutes/seconds.
	 */
	public static String formatElapsedTime(long millis) {
		long seconds = millis / 1000;
		long minutes = seconds / 60;
		Object[] args = { new Long(minutes), new Long(seconds % 60) };
		return MINUTE_SECONDS.format(args);
	}

	/**
	 * return a lenient date format set to GMT time zone.
	 * 
	 * @param pattern
	 *            the pattern used for date/time formatting.
	 * @return the configured format for this pattern.
	 */
	private static DateFormat createDateFormat(String pattern) {
		SimpleDateFormat sdf = new SimpleDateFormat(pattern);
		return sdf;
	}

	/**
	 * Calculate the phase of the moon for a given date.
	 * 
	 * <p>
	 * Code heavily influenced by hacklib.c in <a
	 * href="http://www.nethack.org/">Nethack</a>
	 * </p>
	 * 
	 * <p>
	 * The Algorithm:
	 * 
	 * <pre>
	 * moon period = 29.53058 days ~= 30, year = 365.2422 days
	 * 
	 * days moon phase advances on first day of year compared to preceding year
	 *  = 365.2422 - 12*29.53058 ~= 11
	 * 
	 * years in Metonic cycle (time until same phases fall on the same days of
	 *  the month) = 18.6 ~= 19
	 * 
	 * moon phase on first day of year (epact) ~= (11*(year%19) + 18) % 30
	 *  (18 as initial condition for 1900)
	 * 
	 * current phase in days = first day phase + days elapsed in year
	 * 
	 * 6 moons ~= 177 days
	 * 177 ~= 8 reported phases * 22
	 * + 11/22 for rounding
	 * </pre>
	 * 
	 * @return The phase of the moon as a number between 0 and 7 with 0 meaning
	 *         new moon and 4 meaning full moon.
	 * 
	 * @since 1.2, Ant 1.5
	 */
	public static int getPhaseOfMoon(Calendar cal) {
		int dayOfTheYear = cal.get(Calendar.DAY_OF_YEAR);
		int yearInMetonicCycle = ((cal.get(Calendar.YEAR) - 1900) % 19) + 1;
		int epact = (11 * yearInMetonicCycle + 18) % 30;
		if ((epact == 25 && yearInMetonicCycle > 11) || epact == 24) {
			epact++;
		}
		return (((((dayOfTheYear + epact) * 6) + 11) % 177) / 22) & 7;
	}

	/**
	 * Returns the current Date in a format suitable for a SMTP date header.
	 * 
	 * @since Ant 1.5.2
	 */
	public static String getDateForHeader() {
		Calendar cal = Calendar.getInstance();
		TimeZone tz = cal.getTimeZone();
		int offset = tz.getOffset(cal.get(Calendar.ERA),
				cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.DAY_OF_WEEK),
				cal.get(Calendar.MILLISECOND));
		StringBuffer tzMarker = new StringBuffer(offset < 0 ? "-" : "+");
		offset = Math.abs(offset);
		int hours = offset / (60 * 60 * 1000);
		int minutes = offset / (60 * 1000) - 60 * hours;
		if (hours < 10) {
			tzMarker.append("0");
		}
		tzMarker.append(hours);
		if (minutes < 10) {
			tzMarker.append("0");
		}
		tzMarker.append(minutes);
		return DATE_HEADER_FORMAT.format(cal.getTime()) + tzMarker.toString();
	}

	/**
	 * Parse a string as a datetime using the ISO8601_DATETIME format which is
	 * <code>yyyy-MM-dd'T'HH:mm:ss</code>
	 * 
	 * @param datestr
	 *            string to be parsed
	 * 
	 * @return a java.util.Date object as parsed by the format.
	 * @exception ParseException
	 *                if the supplied string cannot be parsed by this pattern.
	 * @since Ant 1.6
	 */
	public static Date parseIso8601DateTime(String datestr)
			throws ParseException {
		return new SimpleDateFormat(ISO8601_DATETIME_PATTERN).parse(datestr);
	}

	/**
	 * Parse a string as a date using the ISO8601_DATE format which is
	 * <code>yyyy-MM-dd</code>
	 * 
	 * @param datestr
	 *            string to be parsed
	 * 
	 * @return a java.util.Date object as parsed by the format.
	 * @exception ParseException
	 *                if the supplied string cannot be parsed by this pattern.
	 * @since Ant 1.6
	 */
	public static Date parseIso8601Date(String datestr) throws ParseException {
		return new SimpleDateFormat(ISO8601_DATE_PATTERN).parse(datestr);
	}

	/**
	 * Parse a string as a date using the either the ISO8601_DATETIME or
	 * ISO8601_DATE formats.
	 * 
	 * @param datestr
	 *            string to be parsed
	 * 
	 * @return a java.util.Date object as parsed by the formats.
	 * @exception ParseException
	 *                if the supplied string cannot be parsed by either of these
	 *                patterns.
	 * @since Ant 1.6
	 */
	public static Date parseIso8601DateTimeOrDate(String datestr)
			throws ParseException {
		try {
			return parseIso8601DateTime(datestr);
		} catch (ParseException px) {
			return parseIso8601Date(datestr);
		}
	}

	public static int compareDate(Date date1, Date date2) throws ParseException {
		String strDate1 = format(date1, ISO8601_DATE_PATTERN);
		String strDate2 = format(date2, ISO8601_DATE_PATTERN);
		DateFormat df = new SimpleDateFormat(ISO8601_DATE_PATTERN);
		date1 = df.parse(strDate1);
		date2 = df.parse(strDate2);
		return date1.compareTo(date2);

	}
	public static int compareDateNoTime(Date date1, Date date2) throws ParseException {
		DateFormat df = new SimpleDateFormat(ISO8601_DATE_PATTERN);
		date1 = df.parse(df.format(date1));
		date2 = df.parse(df.format(date2));
		return date1.compareTo(date2);

	}

	public static Date getOnlyDDMMYYYY(Date date) throws Exception {
		String dateStr = format(date, "dd/MM/yyyy");
		return createDateDDMMYYYY(dateStr);
	}

	public static Date createDate(String dateStr) throws Exception {
		DateFormat df = new SimpleDateFormat(pattern);
		return df.parse(dateStr);
	}

	public static Date createDateDDMMYYYY(String dateStr) throws Exception {
		DateFormat df = new SimpleDateFormat("dd/MM/yyyy");
		return df.parse(dateStr);
	}

	public static Date createDateYYYYMMDD(String dateStr) throws Exception {
		DateFormat df = new SimpleDateFormat("yyyyMMdd");
		return df.parse(dateStr);
	}

	public static void main(String agr[]) throws Exception {
		Date date = createDateDDMMYYYY("12/08/2012");
		System.out.println(date);

		String f = DateUtils.format(date, pattern);
		System.out.println(f);

		int v = DateUtils.compareDate(date, createDate("20120326203012"));
		System.out.println(v);
	}

	public static Date dateInfuture(Date today, int days) {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(today);
		days = days - 1;
		calendar.add(Calendar.DATE, days);
		return calendar.getTime();
	}

	@SuppressWarnings("static-access")
	public static Date workingDayInfuture(Date today, int days) {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(today);
		days = days - 1;
		calendar.add(Calendar.DATE, days);
		int day = calendar.get(calendar.DAY_OF_WEEK);
		if (day == 1) {
			calendar.add(Calendar.DATE, 1);
		}
		if (day == 7) {
			calendar.add(Calendar.DATE, 2);
		}
		return calendar.getTime();
	}
	@SuppressWarnings("static-access")
	public static Date nextMonth(Date today, int month) {

		Calendar calendar = Calendar.getInstance();
		calendar.setTime(today);
		calendar.add(Calendar.MONTH, month);
		int day = calendar.get(calendar.DAY_OF_WEEK);
		if (day == 1) {
			calendar.add(Calendar.DATE, 1);
		}
		if (day == 7) {
			calendar.add(Calendar.DATE, 2);
		}
		return calendar.getTime();
	}
}
